using System;
using System.Collections.Generic;
using System.IO;
using Clandestine.Audio;

namespace nicotine
{
	public partial class Interpreter
	{
		Audio audio;
		Sound music = null;
		
		Dictionary<string, int> labels = new Dictionary<string, int>();
		Dictionary<string, bool> flags = new Dictionary<string, bool>();
		Dictionary<string, int> counters = new Dictionary<string, int>();
		Stack<int> stack = new Stack<int>();
		
		Dictionary<string, string> actionDescriptions = new Dictionary<string, string>();
		Dictionary<string, string> actionActions = new Dictionary<string, string>();
		
		List<string> lines;
		
		int currLine;
		
		public Interpreter(List<string> file, Audio aud)
		{
			this.audio = aud;
			this.lines = file;
			setup();
		}
		
		public void Run()
		{
			for (currLine = 4; currLine < lines.Count; currLine++)
				runLine(lines[currLine]);
		}
		
		private void runLine(string line)
		{
			string strong = line.Trim();
			
			if (strong == "" || strong.StartsWith("#") || strong.StartsWith("//")) // Empty line/comment?
				return;
			
			string[] bits = strong.Split(' ');
			
			if (bits[0].EndsWith(":")) // Label?
				return;
			
			// Otherwise, try and run the damn command!
			switch (bits[0].ToLower())
			{
				case "p":
				case "print":
					print(bits[1], int.Parse(bits[2]), strong.Substring(bits[0].Length + bits[1].Length + bits[2].Length + 3));
					break;
				case "fx":
					fx(int.Parse(bits[1]));
					break;
				
				case "if":
				case "ifn":
					bool not = (bits[0].ToLower() == "ifn");
				
					if (bits[1].ToLower() == "flag" || bits[1].ToLower() == "f")
						ifFlag(not, bits[2], strong.Substring(bits[0].Length + bits[1].Length + bits[2].Length + 3));
					else if (bits[1].ToLower() == "counter" || bits[1].ToLower() == "c")
						ifCounter(not, bits[2], bits[3], int.Parse(bits[4]), strong.Substring(
										bits[0].Length + bits[1].Length + bits[2].Length + bits[3].Length + bits[4].Length + 5));
					else
						throw new Exception("Error in file at line " + currLine.ToString() + ": expected flag/counter, got " + bits[1] + "!");
				
					break;
				
				case "set":
					if (bits[1].ToLower() == "flag" || bits[1].ToLower() == "f")
						setFlag(bits[2], bool.Parse(bits[3]));
					else if (bits[1].ToLower() == "counter" || bits[1].ToLower() == "c")
						setCounter(bits[2], int.Parse(bits[3]));
					else
						throw new Exception("Error in file at line " + currLine.ToString() + ": expected flag/counter, got " + bits[1] + "!");
					break;
					
				case "add":
					addCounter(bits[1], int.Parse(bits[2]));
					break;
					
				case "sub":
					subCounter(bits[1], int.Parse(bits[2]));
					break;
					
				case "mul":
					mulCounter(bits[1], int.Parse(bits[2]));
					break;
					
				case "div":
					divCounter(bits[1], int.Parse(bits[2]));
					break;
					
				case "inc":
					incCounter(bits[1]);
					break;
					
				case "dec":
					decCounter(bits[1]);
					break;
					
				case "j":
				case "jmp":
				case "jump":
					jump(bits[1]);
					break;
					
				case "c":
				case "call":
					call(bits[1]);
					break;
					
				case "r":
				case "rtn":
				case "return":
					rtn();
					break;
					
				case "action":
					if (bits[2].ToLower() == "desc" || bits[2].ToLower() == "description" || bits[2].ToLower() == "print" || bits[2].ToLower() == "p")
						actionDescription(bits[1], strong.Substring(bits[0].Length + bits[1].Length + bits[2].Length + 3));
					else if (bits[2].ToLower() == "action" || bits[2].ToLower() == "a" || bits[2].ToLower() == "do" || bits[2].ToLower() == "x")
						actionDo(bits[1], strong.Substring(bits[0].Length + bits[1].Length + bits[2].Length + 3));
					else
						throw new Exception("Error at line " + currLine.ToString() + ": expected desc/description/print/p or do/x/action/a, got `" + bits[2] + "'.");
						
					break;
					
				case "?":
				case "prompt":
				case "prmpt":
					prompt(bits.Length > 1 ? int.Parse(bits[1]) : 0);
					break;
				
				case "m":
				case "music":
					setMusic(bits[1]);
					break;
					
				default:
					throw new Exception("Can't understand command `" + strong + "' at line " + currLine.ToString());
			}
		}
		
		
		
		private void setup()
		{
			// 1. Read header.
			if (lines[0] != "nico interpreter file format 0 adventurism")
				throw new Exception("Couldn't understand the header properly.  Re-write your header or ensure this game is a valid game.");
			
			readTitle(lines[1]);
			readCounters(lines[2].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries));
			readFlags(lines[3].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries));
			readLabels();
		}
		
		private void readLabels()
		{
			for (int i = 4; i < lines.Count; i++)
				if (lines[i].Length > 2 && lines[i].Contains(":"))
				{
					string[] bits = lines[i].Trim().Split(' ');
				
					if (bits[0].EndsWith(":")) // _is_ it a label?
						labels.Add(bits[0].Substring(0, bits[0].Length - 1), i); // hooray, yes!
				}
		}
		
		private void readCounters(string[] bits)
		{
			if (bits[0] != "counters:")
				throw new Exception("counters: definition could not be found on the appropriate line!");
			
			for (int i = 1; i < bits.Length; i++)
				counters.Add(bits[i], 0);
		}
		
		private void readFlags(string[] bits)
		{
			if (bits[0] != "flags:")
				throw new Exception("flags: definition could not be found on the appropriate line!");
			
			for (int i = 1; i < bits.Length; i++)
				flags.Add(bits[i], false);
		}
		
		private void readTitle(string line)
		{
			if (!line.StartsWith("title: "))
				throw new Exception("title: definition could not be found on the appropriate line!");
			
			Console.Title = line.Substring(7);
		}
	}
}
